最新 | 用深度强化学习打造不亏钱的交易机器人(附代码)
作者:Adam King
编译:公众号海外编辑部
近期原创文章:
♥ 基于无监督学习的期权定价异常检测(代码+数据)
♥ 5种机器学习算法在预测股价的应用(代码+数据)
♥ 深入研读:利用Twitter情绪去预测股市
♥ Two Sigma用新闻来预测股价走势,带你吊打Kaggle
♥ 利用深度学习最新前沿预测股价走势
♥ 一位数据科学PhD眼中的算法交易
♥ 基于RNN和LSTM的股市预测方法
♥ 人工智能『AI』应用算法交易,7个必踩的坑!
♥ 神经网络在算法交易上的应用系列(一)
♥ 预测股市 | 如何避免p-Hacking,为什么你要看涨?
♥ 如何鉴别那些用深度学习预测股价的花哨模型?
♥ 优化强化学习Q-learning算法进行股市
前言
在本文中,我们将创建深度强化学习agents,学习如何通过比特币交易赚钱。在本文中,我们将使用OpenAI的gym和来自stable-baselines库的PPOagent,这是OpenAI baselines 库的一个分支。
十分感谢OpenAI和DeepMind在过去几年为深度学习研究人员提供的开源软件。
正如Teddy Roosevelt所说:
Nothing worth having comes easy.
所以,与其学习如何交易自己,不如制造一个机器人来为我们做这件事。本文的标的将应用在比特币上。
获取全部代码,见文末
正文
本文中,我们将使用Zielak生成的Kaggle数据集。.csv数据文件在这里下载:
https://www.kaggle.com/mczielinski/bitcoin-historical-data
首先,让我们导入所有必要的库。
接下来,让我们为环境创建类。我们将需要传入一个 pandas dataframe,以及一个可选的 initial_balance 和一个l ookback_window_size,它将指示agent在过去的每一步将观察多少时间步长。我们将把每笔交易的佣金默认为0.075%,即Bitmex的当前利率,把 serial 参数默认为false,这意味着我们的dataframe将在默认情况下以随机切片的形式遍历。
我们还在数据帧上调用dropna() 和 r eset_index() 来首先删除任何带有 NaN 值的行,然后重置frame的索引,因为我们已经删除了数据。
这里的 action_space 表示为一个由3个选项组成的离散集合(买进、卖出或持有)和另一个由10个数量组成的离散集合(1/10、2/10、3/10,等等)。当购买操作被选中时,我们将购买 amount * self.balance 平衡BTC值。对于卖出操作,我们将卖出 amount * self.btc_held 持有BTC的价值。当然,hold动作将忽略数量,什么也不做。
我们的observation_space被定义为0到1之间的连续浮点数集,其大小为(10, lookback_window_size + 1)。+1 表示当前的时间步长。对于窗口中的每个时间步长,我们将观察OHCLV值、我们的净资产、BTC买卖的金额以及在这些BTC上花费或收到的总额。
接下来,我们需要编写reset方法来初始化环境。
这里我们同时使用 self._reset_session 和 self._next_observation,让我们来定义它们。
交易时段
我们环境的一个重要部分是交易时段的概念。如果我们将这个agent部署到外部,我们可能永远不会一次运行它超过几个月。 出于这个原因,我们将限制 self.df 中连续frames的数量,我们的agent将连续看到这些帧frames。
在我们的 _reset_session 方法中,我们首先将 current_step 重置为0。接下来,我们将 steps_left 设置为一个介于1和 MAX_TRADING_SESSION 之间的随机数,现在我们将在代码最前面定义这个随机数。
接下来,如果我们连续遍历frame,我们将设置要遍历的整个frame,否则我们将 frame_start 设置为 self.df 中的随机点,并创建一个名为 active_df 的新数frame,它只是一个切片 self.df 从 frame_start 到 frame_start + steps_left 。
在随机切片中遍历dataframe的一个不好的地方是,当经过长时间的训练,我们的agent将有更多独特的数据可用。例如,如果我们只以串行方式遍历dataframe(即从 0 到 len(df) 的顺序),那么我们只会拥有与dataframe中相同数量的惟一数据点。我们的观测空间甚至只能在每一个时间步长上呈现出离散的状态。
然而,通过随机遍历dataframe的各个部分,我们通过为初始数据集中的每个时间步创建更有趣的帐户余额、所进行的交易和以前看到的价格行为组合,本质上生成了更独特的数据点。
在重置串行环境后的时间步骤10中,我们的agent将始终在dataframe中的同一时间内,并且在每个时间步骤中有3个选择:买进、卖出或持有。对于这三个选项中的每一个,都需要另外一种选择:10%、20%、……或者100%。这意味着我们的代理可以经历任何(1⁰³)¹⁰总状态,总共1⁰³⁰可能的独特体验。
现在考虑随机切片的环境。在第10步时,我们的agent可以位于dataframe中的任何 len(df) 时间步长。给定在每个时间步骤做出的相同选择,这意味着该代理可以在相同的10个时间步骤中经历任何 len (df)³⁰ 可能的唯一状态。
虽然这可能会给大型数据集增加相当多的噪音,但我们认为它应该允许agent从我们有限的数据量中学到更多。我们仍将以串行方式遍历测试数据集,以便更准确地理解算法对新颖申万、看似“实时”数据的有用性。
Agent Eyes
它通常有助于可视化环境的观察空间,以便了解你的agent将使用哪些类型的特征。例如,下面是使用OpenCV呈现的观察空间的可视化。
OpenCV visualization of the environment’s observation space
图像中的每一行代表我们的observation_space中的一行。 前4行类似频率的红线代表OHCL数据,正下方的虚假橙色和黄色点代表成交量。 下方波动的蓝色条形是agent的净值,而下方的较轻的条形代表agent的交易。
我们将定义_next_observation方法,将观察到的数据从0缩放到1。
It’s important to only scale the data the agent has observed so far to prevent look-ahead biases.
采取行动
现在我们已经建立了我们的观察空间,是时候编写我们的 step 函数了,然后,执行agent的指定操作。当我们当前交易时段的 self.steps_left == 0 时,我们将出售所持有的任何BTC,并调用 _reset_session()。否则,我们将 reward 设置为当前的净资产,并且只有当我们的钱用完时才将 done 设置为 True。
采取行动与 current_price、确定指定的操作以及买卖指定数量的BTC一样简单。让我们快速编写 _take_action,以便测试我们的环境。
最后,用同样的方法,我们将把交易附加到 self.trades 和更新我们的净值和帐户历史。
我们的agent现在可以启动一个新环境,遍历该环境,并采取影响该环境的操作。是时候看他们交易了。
交易机器人
我们的 render 方法可以像调用 print(self.net_worth) 这样简单,但这并不有趣。相反,我们将绘制一个简单的烛台图表。
我们将使用 StockTradingGraph.py 中的代码,并利用它来呈现我们的比特币环境。
我们要做的第一个改变是将 self.df ['Date'] 更新到 self.df ['Timestamp']。并删除所有对 date2num 的调用,因为我们的日期已经采用unix时间戳格式。接下来,在我们的 render 方法中,我们将更新日期标签,以打印我们可读的日期,而不是数字。
首先,导入 datetime 库,然后使用 utcfromtimestamp 方法从每个时间戳获取UTC字符串,并使用 strftime 将字符串格式化为 Y-m-d H:M 格式。
最后,我们将 self.df df['Volume'] 更改为 self.df['Volume_(BTC)'] 来匹配我们的数据集。回到 BitcoinTradingEnv 中,我们现在可以编写渲染方法来显示图形。
绿色表示BTC买入,红色表示卖出。右上角的白色标签是agent当前的净资产,右下角的标签是比特币当前的价格。现在,是时候训练我们的agent了,看看我们能赚多少钱!
训练时间
一种常见的交叉验证形式称为k-fold 验证,在这种验证中,将数据分成k个相等的组,逐个单独将一个组作为测试组,并使用其余的数据作为训练组。然而,时间序列数据高度依赖于时间,这意味着以后的数据高度依赖于以前的数据。所以k-fold行不通,因为我们的agent会在交易之前从未来的数据中学习,这是一个不公平的优势。
当应用于时间序列数据时,同样的缺陷也适用于大多数其他交叉验证策略。因此,我们只需要从整个dataframe中取一小部分作为训练集,从frame的开始到任意索引,然后使用其余的数据作为测试集。
接下来,由于我们的环境只设置为处理单个dataframe,所以我们将创建两个环境,一个用于训练数据,一个用于测试数据。
现在,训练我们的模型就像用我们的环境创建一个代理并调用 model.learn 一样简单。
在这里,我们使用了tensorboard,这样我们就可以很容易地可视化我们的tensorflow图,并查看关于代理的一些量化指标。例如,这里有一张图表,显示了许多agent在20万步以上的时间内获得的rewards:
看来我们的agent非常赚钱!我们最好的agent甚至能够在20万步的过程中使他的余额增加1000倍,而其余的平均至少增加了30倍!
就在这个时候,我们意识到环境中存在一个bug……在修复了这个bug之后,下面是新的图:
正如你所看到的,我们的一些agent做得很好,而其余的人都破产了。然而,表现良好的agent最多能够将初始余额提高10倍甚至60倍。我们必须承认,所有盈利的agent都是在没有佣金的环境下接受训练和测试的,所以我们的agent想要赚到真正的钱还是完全不现实的。
让我们在测试环境中测试我们的agent(使用他们从未见过的新数据),看看他们学习如何交易比特币。
显然,我们还有很多工作要做。通过简单转换我们的模型使用stable-baseline A2C,而不是当前PPO2 agent,我们可以大大提高我们在此数据集上的性能。最后,根据Sean O'Gorman的建议,我们可以略微更新我们的奖励功能,以便我们奖励净值增加,而不仅仅是实现高净值并留在那里。
仅这两个变化就极大地提高了相同数据集上的性能,正如你在下面看到的,我们最终能够在训练集之外的新数据上实现盈利。
我们可以做得更好。为了改善这些结果,我们需要优化超参数,并对agent进行更长时间的训练。
在后续的文章中,我们将使用贝叶斯优化来为我们的问题空间划分最佳超参数,并为使用CUDA对GPU进行训练/测试准备环境。
推荐一篇文章:https://arxiv.org/pdf/1807.02811.pdf
结论
虽然我们还没有完全成功地利用新数据打造出一个盈利的比特币交易机器人,但我们已经比当初设定目标时更接近目标了。下一次,我们将确保我们的agent能够从测试数据上赚钱,而不仅仅是训练数据。
原文:
https://towardsdatascience.com/creating-bitcoin-trading-bots-that-dont-lose-money
推荐阅读
08、Facebook开源神器Prophet预测时间序列基于Python
09、Facebook开源神器Prophet预测股市行情基于Python
10、2018第三季度最受欢迎的券商金工研报前50(附下载)
12、Markowitz有效边界和投资组合优化基于Python
15、预测股市崩盘基于统计机器学习与神经网络(Python+文档)
19、机器学习、深度学习、量化金融、Python等最新书籍汇总下载
如何获取代码
在后台输入(严格大小写)
Trader-RL
—End—
量化投资与机器学习微信公众号,是业内垂直于Quant、MFE、CST等专业的主流自媒体。公众号拥有来自公募、私募、券商、银行、海外等众多圈内10W+关注者。每日发布行业前沿研究成果和最新资讯。